package cz.zcu.fav.rat.dataaccess.service.impl;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.query.dsl.QueryBuilder;

import cz.zcu.fav.rat.beans.Object;
import cz.zcu.fav.rat.beans.ObjectClass;

@Service
@Transactional(readOnly = true)
public class SearchService
{

	private static final Logger logger = Logger.getLogger(SearchService.class);

	@Autowired
	SessionFactory sessionFactory;

	@PostConstruct
	protected void init()
	{

		Session session = sessionFactory.openSession();
		FullTextSession fullTextSession = Search.getFullTextSession(session);
		try
		{
			fullTextSession.createIndexer(Object.class, ObjectClass.class)
					.startAndWait();
		} catch (InterruptedException ex)
		{
			logger.warn("Indexer was unexpectly interrupted");
		} finally
		{
			session.close();
		}
	}

	private List<Object> searchForObjectByName(String queryString,
			FullTextSession fullTextSession)
	{
		List<Object> objectList;

		QueryBuilder queryBuilder = fullTextSession.getSearchFactory()
				.buildQueryBuilder().forEntity(Object.class).get();
		org.apache.lucene.search.Query luceneQuery = queryBuilder.keyword()
				.onFields("name").matching(queryString).createQuery();
		org.hibernate.Query fullTextQuery = fullTextSession
				.createFullTextQuery(luceneQuery, Object.class);

		objectList = fullTextQuery.list();
		return objectList;
	}

	private List<Object> searchForObjectByClassName(String queryString,
			FullTextSession fullTextSession)
	{
		List<ObjectClass> objectClassList;
		List<Object> objects = new ArrayList<>();

		QueryBuilder queryBuilder = fullTextSession.getSearchFactory()
				.buildQueryBuilder().forEntity(ObjectClass.class).get();
		org.apache.lucene.search.Query luceneQuery = queryBuilder.keyword()
				.onFields("name").matching(queryString).createQuery();
		org.hibernate.Query fullTextQuery = fullTextSession
				.createFullTextQuery(luceneQuery, ObjectClass.class);
		objectClassList = fullTextQuery.list();

		for (ObjectClass objClass : objectClassList)
		{
			objects.addAll(objClass.getObjects());
		}

		return objects;
	}

	private List<Object> searchForObjectByDescription(String queryString,
			FullTextSession fullTextSession)
	{
		QueryBuilder queryBuilder = fullTextSession.getSearchFactory()
				.buildQueryBuilder().forEntity(Object.class).get();
		org.apache.lucene.search.Query luceneQuery = queryBuilder.keyword()
				.onFields("description").matching(queryString).createQuery();

		// wrap Lucene query in a javax.persistence.Query
		org.hibernate.Query fullTextQuery = fullTextSession
				.createFullTextQuery(luceneQuery, Object.class);

		return fullTextQuery.list();
	}

	public List<Object> searchForObject(String queryString, SearchType type)
	{
		Session session = sessionFactory.openSession();
		FullTextSession fullTextSession = Search.getFullTextSession(session);
		List<Object> objectList = new ArrayList<Object>();
		try
		{
			switch (type)
			{
			case ALL:
			{
				objectList.addAll(searchForObjectByName(queryString,
						fullTextSession));
				objectList.addAll(searchForObjectByClassName(queryString,
						fullTextSession));
				objectList.addAll(searchForObjectByDescription(queryString,
						fullTextSession));
				break;
			}
			case CLASS_NAME:
				objectList.addAll(searchForObjectByClassName(queryString,
						fullTextSession));
				break;

			case OBJ_DESCRIPTION:
				objectList.addAll(searchForObjectByDescription(queryString,
						fullTextSession));
				break;
			case OBJ_NAME:
				objectList.addAll(searchForObjectByName(queryString,
						fullTextSession));
				break;
			}

		} finally
		{
			fullTextSession.close();
			if (session.isOpen())
			{
				session.close();
			}
		}

		return objectList;
	}
}
